// ============================================================================
//     _____ __  _      __            _       __      ____
//    / ___// /_(_)____/ /_____  ____| |     / /___ _/ / /
//    \__ \/ __/ / ___/ //_/ _ \/ ___/ | /| / / __ `/ / /
//   ___/ / /_/ / /__/ ,< /  __/ /   | |/ |/ / /_/ / / /
//  /____/\__/_/\___/_/|_|\___/_/    |__/|__/\__,_/_/_/
//
// Copyright (C) by daxnet
// https://gitee.com/daxnet/stickerwall
// MIT License
// ============================================================================

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.IdentityModel.Protocols.Configuration;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Stickerwall.Common;
using Stickerwall.DataAccessors.PgSQL;
using Stickerwall.WebAPI;
using Stickerwall.WebAPI.HealthChecks;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddControllers(options =>
{
    options.InputFormatters.Insert(0, Utils.GetJsonPatchInputFormatter());
    options.SuppressAsyncSuffixInActionNames = false;
}).AddNewtonsoftJson();

builder.Services.AddRouting(options =>
{
    options.LowercaseUrls = true;
    options.LowercaseQueryStrings = true;
});

builder.Services.AddHealthChecks()
    .AddCheck<HealthCheckLight>("health-light", tags: ["health-light"])
    .AddCheck<HealthCheckFull>("health-full", tags: ["health-full"]);

// AuthN & AuthZ
var authority = builder.Configuration["keycloak:authority"];
if (string.IsNullOrEmpty(authority)) throw new InvalidConfigurationException("Authority is not configured.");

var realm = builder.Configuration["keycloak:realm"];
if (string.IsNullOrEmpty(realm)) throw new InvalidConfigurationException("Realm is not configured.");

var validateIssuer = Convert.ToBoolean(builder.Configuration["keycloak:validateIssuer"] ?? "true");
var validateAudience = Convert.ToBoolean(builder.Configuration["keycloak:validateAudience"] ?? "true");
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = $"{authority}/realms/{realm}";
        options.MetadataAddress =
            $"{authority}/realms/{realm}/.well-known/openid-configuration";
        options.RequireHttpsMetadata =
            Convert.ToBoolean(builder.Configuration["keycloak:requireHttpsMetadata"] ?? "false");
        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = builder.Configuration["keycloak:nameClaimType"] ?? "preferred_username",
            RoleClaimType = ClaimTypes.Role,
            ValidateIssuer = validateIssuer,
            ValidateAudience = validateAudience
        };
    });

var dbConnectionString = builder.Configuration["db:connectionString"];
builder.Services.AddSingleton<IDataAccessor>(sp =>
    new PgSqlDataAccessor(dbConnectionString, sp.GetRequiredService<ILogger<PgSqlDataAccessor>>()));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "Stickerwall Backend Service",
        Version = "v1"
    });

    var securityScheme = new OpenApiSecurityScheme
    {
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        Description = "Enter Bearer token",
        Scheme = JwtBearerDefaults.AuthenticationScheme,
        Reference = new OpenApiReference
        {
            Id = JwtBearerDefaults.AuthenticationScheme,
            Type = ReferenceType.SecurityScheme
        }
    };

    c.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, securityScheme);
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            securityScheme, new List<string>()
        }
    });
});

builder.Services.AddCors(options =>
{
    options.AddPolicy("CorsPolicy", pb =>
    {
        pb.AllowAnyOrigin();
        pb.AllowAnyHeader();
        pb.AllowAnyMethod();
    });
});

var app = builder.Build();

app.MapHealthChecks("/health-light", new HealthCheckOptions
{
    Predicate = hc => hc.Tags.Contains("health-light")
});

app.MapHealthChecks("/health-full", new HealthCheckOptions
{
    Predicate = hc => hc.Tags.Contains("health-full"),
    ResponseWriter = Utils.WriteHealthCheckResponse
});

app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapControllers();

app.Run();